#region Using References

using System;
using System.Diagnostics.Contracts;

#endregion

namespace Beaker.OpenCube.Game
{
	/// <summary>
	/// Rotation of an object, item or entity
	/// </summary>
	public struct Rotation
		: IEquatable<Rotation>
	{

		/// <summary>
		/// The default empty Rotation value
		/// </summary>
		public static readonly Rotation Empty = new Rotation();

		/// <summary>
		/// Initializes a new instance of the <see cref="Rotation" /> struct.
		/// </summary>
		/// <param name="yaw">The yaw.</param>
		/// <param name="pitch">The pitch.</param>
		public Rotation( float yaw, float pitch )
		{
			Contract.Requires<ArgumentOutOfRangeException>( pitch >= -90f );
			Contract.Requires<ArgumentOutOfRangeException>( pitch <= 90f );
			Contract.Ensures( Contract.ValueAtReturn( out _pitch ) == pitch );
			Contract.Ensures( Contract.ValueAtReturn( out _yaw ) == yaw );

			_pitch = pitch;
			_yaw = yaw;
		}

		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( _pitch >= -90f );
			Contract.Invariant( _pitch <= 90f );
		}

		private readonly float _yaw;

		/// <summary>
		/// Gets or sets the yaw (rotation clockwise around the Y axis).
		/// </summary>
		/// <value>
		/// The yaw.
		/// </value>
		/// <remarks>Due west is 0. Can have large values because
		/// it accumulates all of the entity's lateral rotation throughout the game.</remarks>
		public float Yaw
		{
			get
			{
				Contract.Ensures( Contract.Result<float>().Equals( _yaw ) );
				return _yaw;
			}
		}

		private readonly float _pitch;

		/// <summary>
		/// Gets or sets the pitch (declination from the horizon).
		/// </summary>
		/// <value>
		/// The pitch.
		/// </value>
		/// <remarks>Horizontal is 0. Positive values look downward. 
		/// Does not exceed positive or negative 90 degrees.</remarks>
		public float Pitch
		{
			get
			{
				Contract.Ensures( Contract.Result<float>() >= -90f );
				Contract.Ensures( Contract.Result<float>() <= 90f );
				Contract.Ensures( Contract.Result<float>().Equals( _pitch ) );

				return _pitch;
			}
		}

		/// <summary>
		/// Determines whether the specified <see cref="System.Object" /> is equal to this rotation.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object" /> to compare with this rotation.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this rotation; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals( object obj )
		{
			if( ReferenceEquals( null, obj ) ) return false;
			return obj is Rotation && Equals( (Rotation)obj );
		}

		/// <summary>
		/// Determines whether the specified <see cref="Rotation" /> is equal to this rotation.
		/// </summary>
		/// <param name="other">The <see cref="Rotation" /> to compare with this rotation.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="Rotation" /> is equal to this rotation; otherwise, <c>false</c>.
		/// </returns>
		public bool Equals( Rotation other )
		{
			Contract.Ensures( Contract.Result<bool>() == false || _yaw.Equals( other._yaw ) && _pitch.Equals( other._pitch ) );
			return _yaw.Equals( other._yaw ) && _pitch.Equals( other._pitch );
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			unchecked
			{
				return ( _yaw.GetHashCode() * 397 ) ^ _pitch.GetHashCode();
			}
		}

		public static bool operator ==( Rotation left, Rotation right )
		{
			return left.Equals( right );
		}

		public static bool operator !=( Rotation left, Rotation right )
		{
			return !left.Equals( right );
		}
	}
}